package org.wak.scene
{
	import flash.display.DisplayObjectContainer;
	import flash.display.Sprite;
	import flash.events.EventDispatcher;
	
	import org.wak.scene.events.SceneEvent;
	
	
	/**
	 * This is the manager of Scenes used in the game. 
	 */	
	public class SceneManager extends EventDispatcher
	{
		// Layer where the Scenes will be added.
		private var mScenesLayer:Sprite ;
		
		// Asociative Array by id with Scenes definitions extracted from mLocDefs.
		private var mLocDefsArray:Array ;
		
		// Asociative Array with the Scenes instanciated.  
		private var mScenesArray:Array ;
		
		// XML of the Scenes
		private var mLocDefs:XML ;
		
		// Current Active Scene 
		private var mCurrentScene:Scene = null;
		
		private var mEnabled:Boolean = true ;
		
		/**
		 * Constructor 
		 * 
		 * @param pLayer, the layer where the Scenes will be added.
		 */		
		public function SceneManager( pLayer:Sprite )
		{
			super();
			mScenesLayer = pLayer ;
			mLocDefsArray = new Array();
			mScenesArray = new Array();
		}
		
		/**
		 *  Gets the scene with id pId.
		 * @param pId
		 * 
		 */		
		public function GetScene(pId:String):Scene
		{
			var fReturnScene:Scene = null			
			if(mScenesArray[pId] == null )
			{
				var pLocDef:ISceneDefinition = mLocDefsArray[pId] as ISceneDefinition ;
				
				if(pLocDef == null)
				{
					throw new Error(this+": Unknown Scene id "+ pId);
				}
				else
				{
					mScenesArray[pId] = new Scene(pLocDef);
				}
			}
			fReturnScene = mScenesArray[pId] ;
			return fReturnScene ;
		}
		
		/**
		 * This method has the same effect that SetScene but it is used where
		 * the player change the scene by walking over a GateSpot. 
		 * @param pGs the GateSpot where the player has walked over.
		 * @param pFromSceneId the scene where the player came from.
		 * 
		 */		
		public function SetSceneFromGateSpot( pGs:GateSpot, pFromSceneId:String ):void
		{
			SetScene(pGs.destinationSceneId);
			mCurrentScene.SetPlayerDisplayFromScene( pFromSceneId );
		}
		
		/**
		 * Set the active Scene with the id stored in pId  
		 * @param pId id of the Scene.
		 * 
		 */		
		public function SetScene(pId:String):void
		{
			if( mCurrentScene != null)
			{
				if( mScenesLayer.contains( mCurrentScene ) )
					mScenesLayer.removeChild( mCurrentScene );
					
				mCurrentScene.OnExitScene();
				RemoveSceneEventListeners(mCurrentScene);
			}
			
			if(mScenesArray[pId] == null )
			{
				var pLocDef:ISceneDefinition = mLocDefsArray[pId] as ISceneDefinition ;
				
				if(pLocDef == null)
				{
					throw new Error(this+": Unknown Scene id "+ pId);
				}
				else
				{
					mScenesArray[pId] = new Scene(pLocDef);
				}
			}
			mCurrentScene = mScenesArray[pId] as Scene;
			SetSceneEventListeners( mCurrentScene );
			mScenesLayer.addChild( mCurrentScene );
			mCurrentScene.OnEnterScene() ;
			
		}
		
		/**
		 * Loads the Scene's definition, but do not instanciate them .  
		 * @param pXMLLocDefs an XML object with the definition of the Scenes.
		 */		
		public function LoadScenes(pXMLLocDefs:XML):void
		{
			mLocDefs = pXMLLocDefs ;
			ExtractDefinitions(mLocDefs, mLocDefsArray);	
		}
		
		public function set enabled(value:Boolean):void
		{
			mEnabled = value ;
		}
		
		public function get enabled():Boolean
		{
			return mEnabled ;
		}
		
		/**
		 * Extracts the Scenes Definitions from the input XML pDefs to an asociative
		 * Array by Scene id. 
		 * @param pDefs XML containing the definitions
		 * @param pDest destination array
		 * 
		 */		
		private function ExtractDefinitions( pDefs:XML, pDest:Array ):void
		{
			var fLocDef:ISceneDefinition = null;
			for each( var def:XML in pDefs.Scene )
			{
				fLocDef = new XMLSceneDefinition( def );
				pDest[fLocDef.sceneId] = fLocDef ;
			}
		}
		
		/**
		 * The display object layer to draw the Scenes 
		 * @return DisplayObjectContainer of the Scenes.
		 * 
		 */		
		public function get SceneLayer():DisplayObjectContainer
		{
			return mScenesLayer ;
		}
		
		/**
		 * Set the event listeners for pScene. 
		 * @param pScene to add event listeners.
		 * 
		 */			 
		private function SetSceneEventListeners( pScene:Scene ):void
		{
			pScene.addEventListener(SceneEvent.ENTER_SCENE, OnEnterScene );
			pScene.addEventListener(SceneEvent.EXIT_SCENE, OnExitScene );
			pScene.addEventListener(SceneEvent.SCENE_SPACE_CLICK, OnSceneSpaceClick );
			pScene.addEventListener(SceneEvent.SCENEITEM_CLICK, OnSceneItemClick );
			pScene.addEventListener(SceneEvent.SCENEITEM_MOUSEOVER, OnSceneItemMouseOver );
			pScene.addEventListener(SceneEvent.SCENEITEM_MOUSEOUT, OnSceneItemMouseOut );
			
		}
		
		/**
		 * Set the event listeners for pScene. 
		 * @param pScene to add event listeners.
		 * 
		 */			 
		private function RemoveSceneEventListeners( pScene:Scene ):void
		{
			pScene.removeEventListener(SceneEvent.ENTER_SCENE, OnEnterScene );
			pScene.removeEventListener(SceneEvent.EXIT_SCENE, OnExitScene );
			pScene.removeEventListener(SceneEvent.SCENE_SPACE_CLICK, OnSceneSpaceClick );
			pScene.removeEventListener(SceneEvent.SCENEITEM_CLICK, OnSceneItemClick );
			pScene.removeEventListener(SceneEvent.SCENEITEM_MOUSEOVER, OnSceneItemMouseOver );
			pScene.removeEventListener(SceneEvent.SCENEITEM_MOUSEOUT, OnSceneItemMouseOut );
		}
		
		
		
		// Event handlers //
		
		/**
		 * SCENEITEM_CLICK Event handler 
		 * @param event a SceneEvent .
		 * 
		 */		
		private function OnSceneItemClick( event:SceneEvent ):void
		{
			if(!mEnabled)
				return ;
			
			var fEvent:SceneEvent = new SceneEvent(event.type,
					event.interactable,
					event.sceneId,
					event.bubbles,
					event.cancelable,
					event.localX,
					event.localY,
					event.ctrlKey,
					event.altKey,
					event.shiftKey,
					event.sceneMouseX,
					event.sceneMouseY ) ;
			
			this.dispatchEvent( fEvent );
		}
		
		/**
		 * SCENEITEM_MOUSEOVER Event handler 
		 * @param event a SceneEvent .
		 * 
		 */	
		private function OnSceneItemMouseOver( event:SceneEvent ):void
		{
			if(!mEnabled)
				return ;
			
			var fEvent:SceneEvent = new SceneEvent(event.type,
					event.interactable,
					event.sceneId,
					event.bubbles,
					event.cancelable,
					event.localX,
					event.localY,
					event.ctrlKey,
					event.altKey,
					event.shiftKey,
					event.sceneMouseX,
					event.sceneMouseY ) ;
			
			this.dispatchEvent( fEvent );
		}
		
		/**
		 * SCENEITEM_MOUSEOUT Event handler 
		 * @param event a SceneEvent .
		 * 
		 */	
		private function OnSceneItemMouseOut( event:SceneEvent ):void
		{
			if(!mEnabled)
				return ;
				
			var fEvent:SceneEvent = new SceneEvent(event.type,
					event.interactable,
					event.sceneId,
					event.bubbles,
					event.cancelable,
					event.localX,
					event.localY,
					event.ctrlKey,
					event.altKey,
					event.shiftKey,
					event.sceneMouseX,
					event.sceneMouseY ) ;
			
			this.dispatchEvent( fEvent );
		}
		
		/**
		 * SCENE_SPACE_CLICK Event handler 
		 * @param event a SceneEvent .
		 * 
		 */	
		private function OnSceneSpaceClick( event:SceneEvent ):void
		{
			if(!mEnabled)
				return ;
				
			var fEvent:SceneEvent = new SceneEvent(event.type,
					event.interactable,
					event.sceneId,
					event.bubbles,
					event.cancelable,
					event.localX,
					event.localY,
					event.ctrlKey,
					event.altKey,
					event.shiftKey,
					event.sceneMouseX,
					event.sceneMouseY ) ;
			
			this.dispatchEvent( fEvent );
		}
		
		/**
		 * ENTER_SCENE Event handler 
		 * @param event a SceneEvent .
		 * 
		 */	
		private function OnEnterScene( event:SceneEvent ):void
		{
			if(!mEnabled)
				return ;
				
			var fEvent:SceneEvent = new SceneEvent(event.type,
					event.interactable,
					event.sceneId,
					event.bubbles,
					event.cancelable,
					event.localX,
					event.localY,
					event.ctrlKey,
					event.altKey,
					event.shiftKey,
					event.sceneMouseX,
					event.sceneMouseY ) ;
			
			this.dispatchEvent( fEvent );
		}
		
		/**
		 * EXIT_SCENE Event handler 
		 * @param event a SceneEvent .
		 * 
		 */	
		private function OnExitScene( event:SceneEvent ):void
		{
			if(!mEnabled)
				return ;
				
			var fEvent:SceneEvent = new SceneEvent(event.type,
					event.interactable,
					event.sceneId,
					event.bubbles,
					event.cancelable,
					event.localX,
					event.localY,
					event.ctrlKey,
					event.altKey,
					event.shiftKey,
					event.sceneMouseX,
					event.sceneMouseY ) ;
			
			this.dispatchEvent( fEvent );
		}
		
		/**
		 * The current scene where the player is. 
		 * @return current Scene
		 * 
		 */		
		public function get currentScene():Scene
		{
			return mCurrentScene ;
		}
		
		

	}
}